﻿#include "FITKGraphObjectMeshPart.h"

// VTK
#include <vtkProperty.h>
#include <vtkIntArray.h>
#include <vtkUnstructuredGrid.h>

// Graph
#include "FITKColorMap.h"
#include "FITKColorMapManager.h"
#include "FITKGraphCommons.h"

 // Case
#include "FITK_Component/FITKAbaqusData/FITKDataCaseManager.h"
#include "FITK_Component/FITKAbaqusData/FITKDataCase.h"
#include "FITK_Component/FITKAbaqusData/FITKAbaqusData.h"

// Data
#include "FITK_Component/FITKAbaqusData/FITKAbaqusPart.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaMeshPartVTK.h"
#include "FITK_Interface/FITKInterfaceModel/FITKUnstructuredMesh.h"
#include "FITK_Interface/FITKInterfaceMesh/FITKMeshFeatureVTK.h"
#include "FITK_Interface/FITKInterfaceModel/FITKComponentManager.h"
#include "FITK_Interface/FITKInterfaceModel/FITKMeshSurfaceElement.h"

// Color assign
#include "FITK_Interface/FITKInterfacePhysics/FITKAbstractSection.h"
#include "FITK_Interface/FITKInterfacePhysics/FITKSectionAssign.h"
#include "FITK_Interface/FITKInterfacePhysics/FITKMaterialAssign.h"

// Graph and filter
#include "FITK_Interface/FITKVTKAlgorithm/FITKHighOrderCellFilter.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKShellFeatureEdges.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphObjectModelHighlighting.h"

namespace Exchange
{
    FITKGraphObjectMeshPart::FITKGraphObjectMeshPart(AbaqusData::FITKDataCase* caseData, AbaqusData::FITKAbaqusPart* part)
        : FITKGraphObjectMeshModelBase(caseData, part)
    {
        // Return if the input is nullptr.
        if (!part)
        {
            return;
        }

        // Initialize.
        init();
        
        // Generate the part actors.
        generateGraph(part);

        // Save the graph information.
        m_graphInfo.Type = PartGraph;
        m_graphInfo.PartId = part->getDataObjectID();

        // Set the default model render mode.
        setRenderModel(ModelViewMode::ModelShaded);
    }

    void FITKGraphObjectMeshPart::generateGraph(AbaqusData::FITKAbaqusPart* part)
    {
        // Get mesh data from the part data.
        m_meshData = part->getMesh();
        if (!m_meshData)
        {
            return;
        }

        // Initialize the color map with the part id.
        m_colorMap = FITKColorMapManager::getInstance()->getPartColorMap(part->getDataObjectID());

        // Get surface data for visualization.
        m_featureMesh = m_meshData->getMeshFeature();
        if (!m_featureMesh)
        {
            return;
        }

        // Update mesh data.
        m_featureMesh->update();

        // Initialize feature actor.
        //@{
        // Create feature actor for shell.
        m_isShellModel = (m_meshData->getMeshDim() == Interface::FITKModelEnum::FITKMeshDim::FMDimD2);
        if (m_isShellModel)
        {
            // Get vtk feature data from the mesh data.
            FITKShellFeatureEdges* edgeFilter = m_featureMesh->getFeatureEdgeFilter();
            if (!edgeFilter)
            {
                return;
            }

            // Create the feature actor and initialize the properties.
            initFeatureActor();

            // Generate FITKGraphActor and set the port of feature filter.
            m_fActorFeature->setInputConnection(edgeFilter->GetOutputPort());
        }
        //@}

        // Get vtk surface data from the mesh data, and save the feature edge filter for simplified model.
        m_highOrderCellFilter = m_featureMesh->getHighOrderFilter();
        m_featureEdgeFilter = m_featureMesh->getFeatureEdgeFilter();
        if (!m_highOrderCellFilter)
        {
            return;
        }

        // Save the model filter.
        m_modelFilter = m_highOrderCellFilter;
        m_modelEdgeFilter = m_featureEdgeFilter;

        // Initialize the actors.
        initActors();

        // Generate FITKGraphActor and set the port of surface filter( high order cell filter ).
        m_fActorFace->setInputConnection(m_highOrderCellFilter->GetOutputPort());
        m_fActorPoint->setInputConnection(m_highOrderCellFilter->GetOutputPort());

        if (m_graphObjHighlight)
        {
            m_graphObjHighlight->setModelInputConnection(m_highOrderCellFilter->GetOutputPort());
        }

        if (m_fActorAddin)
        {
            m_fActorAddin->setInputConnection(m_highOrderCellFilter->GetOutputPort());
            m_fActorAddin->SetVisibility(false);
        }
    }

    void FITKGraphObjectMeshPart::colorByDefault(bool forceUpdate)
    {
        // If the color mode is the same as current mode, return.
        if (m_colorMode == ColorMode::ColorDefault && !forceUpdate)
        {
            return;
        }

        // Set the graph actors with the default model color.
        // Set color.
        colorByDefaultForPart(_dataObj);

        // Set the color mode value.
        m_colorMode = ColorMode::ColorDefault;
    }

    void FITKGraphObjectMeshPart::colorByMaterial(bool forceUpdate)
    {
        // If the color mode is the same as current mode, return.
        if (m_colorMode == ColorMode::ColorMaterial && !forceUpdate)
        {
            return;
        }

        // Set the graph actors with the material's color.
        // Set color.
        colorByMaterialForPart(_dataObj);

        // Set the color mode value.
        m_colorMode = ColorMode::ColorMaterial;
    }

    void FITKGraphObjectMeshPart::colorBySection(bool forceUpdate)
    {
        // If the color mode is the same as current mode, return.
        if (m_colorMode == ColorMode::ColorSection && !forceUpdate)
        {
            return;
        }

        // Set the graph actors with the section's color.
        // Set color.
        colorBySectionForPart(_dataObj);

        // Set the color mode value.
        m_colorMode = ColorMode::ColorSection;
    }

    void FITKGraphObjectMeshPart::colorByDefaultProperty(bool forceUpdate)
    {
        // If the color mode is the same as current mode, return.
        if (m_colorMode == ColorMode::ColorDefaultProperty && !forceUpdate)
        {
            return;
        }

        // Set the graph actors with the property's default color.
        // Get the part data object.
        AbaqusData::FITKAbaqusPart* part = AbaqusData::FITKAbaqusPart::SafeDownCastFITKAbaqusPart(_dataObj);
        if (!m_caseData || !part || !m_fActorFace || !m_colorMap || !m_featureMesh)
        {
            return;
        }

        // Get the section, assign and set manager, and also the model's color map.
        Interface::FITKAbstractSectionManager* secMgr = m_caseData->getSectionManager();
        Interface::FITKSectionAssignManager* assMgr = part->getSectionAssignManager();
        Interface::FITKComponentManager* setMgr = part->getComponentManager();

        if (!secMgr || !assMgr || !setMgr)
        {
            return;
        }

        // Enable show color with scalar.
        //@{
        m_fActorFace->setScalarVisibility(true);
        m_fActorFace->setScalarModeToUseCellFieldData();
        //@}

        // Temp int array for store the cell assigned times.
        //@{
        vtkIntArray* timesArray = vtkIntArray::New();
        int nCell = m_meshData->getNumberOfCells();
        timesArray->SetNumberOfComponents(1);
        timesArray->SetNumberOfValues(nCell);
        timesArray->FillComponent(0, 0);
        //@}

        // Get all assigned sections.
        int nAssign = assMgr->getDataCount();
        for (int i = 0; i < nAssign; i++)
        {
            // Get assign.
            Interface::FITKSectionAssign* assign = assMgr->getDataByIndex(i);
            if (!assign)
            {
                continue;
            }

            // Get set.
            int setId = assign->getModelID();
            Interface::FITKAbstractModelComponent* set = setMgr->getDataByID(setId);

            // Convert to model set.
            Interface::FITKModelSet* modelSet = dynamic_cast<Interface::FITKModelSet*>(set);
            if (!modelSet)
            {
                continue;
            }

            // Get section.
            int secDataId = assign->getSectionID();
            Interface::FITKAbstractSection* sec = secMgr->getDataByID(secDataId);

            // Get element set(s).
            QList<Interface::FITKModelSet*> sets;
            modelSet->getAbsoluteSet(sets, Interface::FITKModelEnum::FITKModelSetType::FMSElem);

            for (Interface::FITKModelSet* s : sets)
            {
                // Get element indice and save to the times array.
                QList<int> indice;
                getSetIds(s, indice);

                // The section is valid.
                if (sec)
                {
                    for (const int & index : indice)
                    {
                        int flag = timesArray->GetValue(index);
                        // flag = flag < 0 ? 0 : flag;
                        timesArray->SetValue(index, flag + 1);
                    }
                }
                // The section has been deleted.
                else
                {
                    for (const int & index : indice)
                    {
                        timesArray->SetValue(index, -1);
                    }
                }
            }
        }

        // Set color with the times array.
        for (int i = 0; i < nCell; i++)
        {
            int times = timesArray->GetValue(i);
            if (times == -1)
            {
                m_colorMap->setCellColor(c_propDefaultArrName, i, FITKGraphCommons::s_notFoundColor);
            }
            else if (times == 0)
            {
                m_colorMap->setCellColor(c_propDefaultArrName, i, FITKGraphCommons::s_defaultMeshColor);
            }
            else if (times == 1)
            {
                m_colorMap->setCellColor(c_propDefaultArrName, i, FITKGraphCommons::s_validColor);
            }
            else
            {
                m_colorMap->setCellColor(c_propDefaultArrName, i, FITKGraphCommons::s_invalidColor);
            }
        }

        // Delete temp array.
        timesArray->Delete();

        // Update the data if neccessary.
        bool needUpdate = m_colorMap->needUpdate();
        if (needUpdate)
        {
            // Update mesh color.
            m_featureMesh->update();

            // Update the color map.
            m_colorMap->update();
        }

        // Switch to the array.
        m_fActorFace->selectScalarArray(c_propDefaultArrName);
        m_fActorFace->update();

        // Set the color mode value.
        m_colorMode = ColorMode::ColorDefaultProperty;
    }

    void FITKGraphObjectMeshPart::colorByDefaultMesh(bool forceUpdate)
    {
        // If the color mode is the same as current mode, return.
        if (m_colorMode == ColorMode::ColorDefaultMesh && !forceUpdate)
        {
            return;
        }

        // Set the graph actors with the section's color.
        // Set color.
        colorByDefaultMeshForPart(_dataObj);

        // Set the color mode value.
        m_colorMode = ColorMode::ColorDefaultMesh;
    }
}   // namespace Exchange